﻿using Engine.Graphics;
using Engine.Graphics.Shaders;
using Engine.Graphics.VertexStructures;
using SharpDX;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace DirectX11Test
{
    public partial class Form1 : Form
    {
        #region Private Fields

        private Texture2D backBuffer;
        private DepthStencilStateDescription depthStencilDesc;
        private DepthStencilState depthStencilState;
        private DepthStencilView depthStencilView;
        private SharpDX.Direct3D11.Device device;
        private SharpDX.Direct3D11.DeviceContext deviceContext;
        private int frames = 0;
        private RenderTargetView renderView;
        private ShaderBase shader1;
        private ShaderBase shader2;
        private ShaderBase shader3;
        private SharpDX.DXGI.SwapChain swapChain;
        private SharpDX.DXGI.SwapChainDescription swapChainDesciption;
        private SharpDX.DXGI.Format swapChainFormat;
        private StandardGraphicBuffers triangle;

        #endregion Private Fields

        #region Public Constructors

        public Form1()
        {
            InitializeComponent();
            this.Load += Form1_Load;
            this.SizeChanged += Form1_SizeChanged;
            this.Show();

            // Load Texture
            var texture1 = Ptx2bmpConverter.Ptx2bmp.ToDDSSlimDX(this.device, "001fels.ptx");
            var texture2 = Ptx2bmpConverter.Ptx2bmp.ToDDSSlimDX(this.device, "002g.ptx");
            var texture3 = Ptx2bmpConverter.Ptx2bmp.ToDDSSlimDX(this.device, "ASPH2.ptx");

            Color4 color1 = new Color4(1, 0, 0, 1);
            Color4 color2 = new Color4(0, 1, 0, 1);
            TestStruct test = new TestStruct() { position = new Vector4(0, 0, 0, 0) };
            // Buffer buffer = new SharpDX.Direct3D11.Buffer(this.device, new BufferDescription(16, ResourceUsage.Dynamic, BindFlags.ConstantBuffer, CpuAccessFlags.Write, ResourceOptionFlags.BufferStructured, 16))
            float x = 0;
            SharpDX.Direct3D11.Buffer buffer = SharpDX.Direct3D11.Buffer.Create(this.device, ref test, new BufferDescription(Marshal.SizeOf(test), ResourceUsage.Dynamic, BindFlags.ConstantBuffer, CpuAccessFlags.Write, ResourceOptionFlags.None, 4));

            float direction = 1;

            DataBox data = this.device.ImmediateContext.MapSubresource(buffer, 0, MapMode.WriteDiscard, MapFlags.None);

            // Set samplerstate
            SamplerStateDescription samplerStateDescription = new SamplerStateDescription()
            {
                AddressU = TextureAddressMode.Wrap,
                AddressV = TextureAddressMode.Wrap,
                AddressW = TextureAddressMode.Wrap,
                BorderColor = new Color4(0),
                ComparisonFunction = Comparison.Always,
                Filter = Filter.Anisotropic,
                MaximumAnisotropy = 16,
                MaximumLod = float.MaxValue,
                MinimumLod = 0,
                MipLodBias = 0
            };

            this.device.ImmediateContext.PixelShader.SetSampler(0, new SamplerState(this.device, samplerStateDescription));
            this.device.ImmediateContext.PixelShader.SetSampler(1, new SamplerState(this.device, samplerStateDescription));
            this.device.ImmediateContext.PixelShader.SetSampler(2, new SamplerState(this.device, samplerStateDescription));

            this.device.ImmediateContext.PixelShader.SetShaderResource(0, texture1);
            this.device.ImmediateContext.PixelShader.SetShaderResource(1, texture2);
            this.device.ImmediateContext.PixelShader.SetShaderResource(2, texture3);

            this.device.ImmediateContext.UnmapSubresource(buffer, 0);
            ShaderBase shader = shader1;
            ShaderBase tempShader;
            do
            {
                test.position39 = new Vector4(1.15555f, 2.0f, 3.333f, 4.4444f);
                if (x > 0.5f || x < -0.50f)
                {
                    tempShader = shader1;
                    shader1 = shader2;
                    shader2 = tempShader;

                    direction *= -1;
                    shader = shader2;

                    this.device.ImmediateContext.Rasterizer.State = new RasterizerState(this.device, new RasterizerStateDescription()
                    {
                        FillMode = FillMode.Wireframe,
                        CullMode = CullMode.None
                    });
                }
                //DataStream data;
                //this.device.ImmediateContext.MapSubresource(buffer, MapMode.WriteDiscard, MapFlags.None, out data);
                //data.Write(test.position);
                //this.device.ImmediateContext.UnmapSubresource(buffer, 0);

                //this.device.ImmediateContext.UpdateSubresource(data, buffer);
                //this.device.ImmediateContext.UnmapSubresource(buffer, 0);

                Utilities.Write(IntPtr.Add(data.DataPointer, 0), ref test);

                x += 0.01f * direction;
                test.position = new Vector4(x, 0, 0, 0);
                Color4 colorTemp = color1;
                color1 = color2;
                color2 = colorTemp;

                this.device.ImmediateContext.ClearRenderTargetView(this.renderView, color1);

                deviceContext.VertexShader.Set(shader.vs);
                deviceContext.PixelShader.Set(shader.ps);

                this.device.ImmediateContext.VertexShader.SetConstantBuffer(0, buffer);
                deviceContext.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(triangle.VertexBuffer, MyOwnVertex.MyPositionNormalTextured.SizeInBytes(), 0));
                deviceContext.InputAssembler.SetIndexBuffer(triangle.IndexBuffer, SharpDX.DXGI.Format.R16_UInt, 0);
                deviceContext.InputAssembler.PrimitiveTopology = SharpDX.Direct3D.PrimitiveTopology.TriangleList;

                deviceContext.DrawIndexed(6, 0, 0);

                // this.device.ImmediateContext.ClearDepthStencilView();

                this.swapChain.Present(1, 0);

                Application.DoEvents();
            } while (this.Visible);

            this.device.ImmediateContext.OutputMerger.Dispose();
            this.device.ImmediateContext.Dispose();
            this.device.Dispose(); this.device = null;
            this.swapChain.Dispose(); this.swapChain = null;
            this.renderView.Dispose(); this.renderView = null;
            this.backBuffer.Dispose(); this.backBuffer = null;
        }

        #endregion Public Constructors

        #region Public Properties

        public SharpDX.Viewport ViewPort { get; set; }

        #endregion Public Properties

        #region Public Methods

        public void Graphic(IntPtr windowControlHandle, int width, int height)
        {
            SharpDX.DXGI.Rational refreshRatw = new SharpDX.DXGI.Rational(60, 1);

            swapChainFormat = SharpDX.DXGI.Format.B8G8R8A8_UNorm;

            swapChainDesciption = new SharpDX.DXGI.SwapChainDescription();
            swapChainDesciption.BufferCount = 1;
            swapChainDesciption.ModeDescription.Height = height;
            swapChainDesciption.ModeDescription.Width = width;
            swapChainDesciption.ModeDescription.Format = swapChainFormat;
            swapChainDesciption.ModeDescription.RefreshRate = new SharpDX.DXGI.Rational(60, 1);
            swapChainDesciption.Usage = SharpDX.DXGI.Usage.RenderTargetOutput;
            swapChainDesciption.OutputHandle = windowControlHandle;
            swapChainDesciption.SwapEffect = SharpDX.DXGI.SwapEffect.Discard;
            swapChainDesciption.IsWindowed = true;
            swapChainDesciption.SampleDescription.Count = 1;
            swapChainDesciption.SampleDescription.Quality = 0;

            //Creates the DirectX Renderdevice.
            Device.CreateWithSwapChain(SharpDX.Direct3D.DriverType.Hardware, DeviceCreationFlags.Debug, swapChainDesciption, out device, out swapChain);
            var context = device.ImmediateContext;
            // create the swap chain, device and device context
            FeatureLevel[] featureLevels = new FeatureLevel[] { FeatureLevel.Level_10_0 };

            var factory = swapChain.GetParent<SharpDX.DXGI.Factory>();
            factory.MakeWindowAssociation(windowControlHandle, SharpDX.DXGI.WindowAssociationFlags.IgnoreAll);

            this.backBuffer = Texture2D.FromSwapChain<Texture2D>(swapChain, 0);
            this.renderView = new RenderTargetView(device, backBuffer);
            this.ViewPort = new SharpDX.Viewport(0, 0, width, height, 0, 1);
            context.Rasterizer.SetViewport(this.ViewPort);
            context.Rasterizer.State = new RasterizerState(this.device, new RasterizerStateDescription()
            {
                CullMode = CullMode.None,
                FillMode = FillMode.Solid,
                IsScissorEnabled = false
            });
            context.OutputMerger.SetTargets(renderView);

            this.deviceContext = this.device.ImmediateContext;

            this.depthStencilDesc = new DepthStencilStateDescription();
            this.depthStencilDesc.IsDepthEnabled = true;
            this.depthStencilDesc.DepthWriteMask = DepthWriteMask.All;
            this.depthStencilDesc.DepthComparison = Comparison.Less;

            this.depthStencilDesc.IsStencilEnabled = true;
            this.depthStencilDesc.StencilReadMask = 0xFF;
            this.depthStencilDesc.StencilWriteMask = 0xFF;

            this.depthStencilDesc.FrontFace.FailOperation = StencilOperation.Keep;
            this.depthStencilDesc.FrontFace.DepthFailOperation = StencilOperation.Increment;
            this.depthStencilDesc.FrontFace.PassOperation = StencilOperation.Keep;
            this.depthStencilDesc.FrontFace.Comparison = Comparison.Always;

            this.depthStencilDesc.BackFace.FailOperation = StencilOperation.Keep;
            this.depthStencilDesc.BackFace.DepthFailOperation = StencilOperation.Decrement;
            this.depthStencilDesc.BackFace.PassOperation = StencilOperation.Keep;
            this.depthStencilDesc.BackFace.Comparison = Comparison.Always;

            //var depthState = new DepthStencilState(this.device, this.depthStencilDesc);
            //this.deviceContext.OutputMerger.SetDepthStencilState(depthState, 0);
        }

        #endregion Public Methods

        #region Private Methods

        private void CreateTriangle()
        {
            // Vertices
            List<Engine.Graphics.VertexStructures.MyOwnVertex.MyPositionNormalTextured> vertices = new List<Engine.Graphics.VertexStructures.MyOwnVertex.MyPositionNormalTextured>();

            vertices.Add(new Engine.Graphics.VertexStructures.MyOwnVertex.MyPositionNormalTextured(new Vector3(0.45f, 0.5f, 0), new Vector3(0, 1, 0), 0, 0.5f, 0, 0.5f, new Color4(1, 1, 1, 1), new Color4(1, 0, 0, 0)));
            vertices.Add(new Engine.Graphics.VertexStructures.MyOwnVertex.MyPositionNormalTextured(new Vector3(0.65f, -0.5f, 0), new Vector3(0, 1, 0), 0.45f, -0.5f, 0.45f, -0.5f, new Color4(1, 1, 1, 0), new Color4(0, 1, 0, 0)));
            vertices.Add(new Engine.Graphics.VertexStructures.MyOwnVertex.MyPositionNormalTextured(new Vector3(-0.45f, -0.5f, 0), new Vector3(0, 1, 0), -0.45f, -0.5f, -0.45f, -0.5f, new Color4(1, 1, 1, 0), new Color4(0, 0, 1, 0)));
            vertices.Add(new Engine.Graphics.VertexStructures.MyOwnVertex.MyPositionNormalTextured(new Vector3(-0.65f, 0.5f, 0), new Vector3(0, 1, 0), 0, 0.5f, 0, 0.5f, new Color4(1, 1, 1, 1), new Color4(1, 0, 0, 0)));
            vertices.Add(new Engine.Graphics.VertexStructures.MyOwnVertex.MyPositionNormalTextured(new Vector3(0.35f, 0.5f, 0), new Vector3(0, 1, 0), 0, 0.5f, 0, 0.5f, new Color4(1, 1, 1, 1), new Color4(1, 0, 0, 0)));

            this.triangle = new StandardGraphicBuffers(this.device);
            this.triangle.UpdateVertexBuffer(vertices.ToArray());
            this.triangle.UpdateIndexBuffer(new ushort[] { 0, 1, 2, 2, 3, 4 });
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Graphic(this.Handle, 800, 600);
            //Graphic(this.Handle, this.Width, this.Height);

            //Create the Triangle
            CreateTriangle();

            // Load Shader
            shader1 = new ShaderBase();
            shader1.LoadShader(this.device, "material00.fx", "Effect_Group_1_Material00_Material00_Pixel_Shader_ps_main", "Effect_Group_1_Material00_Material00_Vertex_Shader_vs_main");
            shader2 = new ShaderBase();
            shader2.LoadShader(this.device, "material01.fx", "Effect_Group_1_Material00_Material00_Pixel_Shader_ps_main", "Effect_Group_1_Material00_Material00_Vertex_Shader_vs_main");
            shader3 = new ShaderBase();
            //shader3.LoadShaderASM(this.device, "material48.fx", "PS_Main", " VertexShader");
            // shader3.LoadShaderASM(this.device, "shader.asm", "PS_Main", " VertexShader");
        }

        private void Form1_SizeChanged(object sender, EventArgs e)
        {
            this.renderView.Dispose();
            this.backBuffer.Dispose();
            this.swapChain.ResizeBuffers(0, this.Width, this.Height, SharpDX.DXGI.Format.R8G8B8A8_UNorm, SharpDX.DXGI.SwapChainFlags.None);
            // this.device.ImmediateContext.OutputMerger.SetRenderTargets(null);
            this.device.ImmediateContext.Rasterizer.SetViewport(new ViewportF(0, 0, this.Width, this.Height, 0, 1));
            swapChainDesciption.ModeDescription.Height = this.Height;
            swapChainDesciption.ModeDescription.Width = this.Width;
            // Device.CreateWithSwapChain(SharpDX.Direct3D.DriverType.Hardware, DeviceCreationFlags.Debug, swapChainDesciption, out device, out swapChain);
            this.backBuffer = Texture2D.FromSwapChain<Texture2D>(swapChain, 0);
            this.renderView = new RenderTargetView(device, backBuffer);
            this.device.ImmediateContext.OutputMerger.SetRenderTargets(renderView);
        }

        #endregion Private Methods

        #region Private Structs

        [StructLayout(LayoutKind.Sequential)]
        private struct TestStruct
        {
            public Vector4 position;
            public Vector4 position1;
            public Vector4 position2;
            public Vector4 position3;
            public Vector4 position4;
            public Vector4 position5;
            public Vector4 position6;
            public Vector4 position7;
            public Vector4 position8;
            public Vector4 position9;
            public Vector4 position10;
            public Vector4 position11;
            public Vector4 position12;
            public Vector4 position13;
            public Vector4 position14;
            public Vector4 position15;
            public Vector4 position16;
            public Vector4 position17;
            public Vector4 position18;
            public Vector4 position19;
            public Vector4 position20;
            public Vector4 position21;
            public Vector4 position22;
            public Vector4 position23;
            public Vector4 position24;
            public Vector4 position25;
            public Vector4 position26;
            public Vector4 position27;
            public Vector4 position28;
            public Vector4 position29;
            public Vector4 position30;
            public Vector4 position31;
            public Vector4 position32;
            public Vector4 position33;
            public Vector4 position34;
            public Vector4 position35;
            public Vector4 position36;
            public Vector4 position37;
            public Vector4 position38;
            public Vector4 position39;
        }

        #endregion Private Structs
    }
}